package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.TeachplanMediaDTO;
import com.xuecheng.api.content.model.CourseBaseDTO;
import com.xuecheng.api.content.model.TeachplanDTO;
import com.xuecheng.api.media.model.MediaDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.AuditEnum;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.common.ResourceTypeEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.api.agent.MediaApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ext.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.mapper.TeachplanMediaMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {
    @Autowired
    private CourseBaseService courseBaseService;
    @Autowired
    private TeachplanMediaService mediaService;
    @Autowired
    private MediaApiAgent mediaApiAgent;
    @Autowired
    private TeachplanMediaService teachplanMediaService;
    @Autowired
    private TeachplanMediaMapper teachplanMediaMapper;
    @Autowired
    private TeachplanService teachplanService;
    @Override
    /*
     * 业务分析：
     * 1.判断关键数据
     * courseId companyId
     * 2.判断业务数据
     * 课程基础信息
     * 判断是否存在
     * 判断是否是同一家教学机构
     * 判断是否删除
     * 3.根据Courseid查询课程计划数据
     * 4.通过java的递归生成课程计划树形结构
     * 5.将数据转为dto并返回
     * */
    public TeachplanDTO queryTreeNodesByCourseId(Long courseId, Long companyId) {
        if (ObjectUtils.isEmpty(companyId) || ObjectUtils.isEmpty(courseId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        LambdaQueryWrapper<CourseBase> baseQueryWrapper = new LambdaQueryWrapper<>();
        baseQueryWrapper.eq(CourseBase::getCompanyId, companyId);
        baseQueryWrapper.eq(CourseBase::getId, courseId);
        CourseBase base = courseBaseService.getOne(baseQueryWrapper);
        if (ObjectUtils.isEmpty(base)){
            ExceptionCast.cast(ContentErrorCode.E_120021);
        }
        Integer status = base.getStatus();
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(status))) {
            ExceptionCast.cast(ContentErrorCode.E_120016);
        }
        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachplanNode> nodes = baseMapper.selectByCourseId(courseId);
        TeachplanDTO teachplanDTO;
        if (CollectionUtils.isEmpty(nodes)) {
            teachplanDTO = new TeachplanDTO();
        } else {
            TeachplanNode rootNode = nodes.remove(0);
            generateTreeNodes(rootNode,nodes);
            teachplanDTO = TeachplanConvert.INSTANCE.node2dto(rootNode);
        }
        // 5.将数据转为dto并返回
        return teachplanDTO;

    }

    @Override
    public TeachplanDTO createOrModifyTeachPlan(TeachplanDTO dto, Long companyId) {
        TeachplanDTO resultDTO = null;
        // 1.判断业务操作
        // 有teachplanid-->修改
        // 没有teachplanid-->新增
        if (ObjectUtils.isEmpty(dto.getTeachPlanId())) {
            // 没有teachplanid-->新增
            resultDTO = createTeachplan(dto,companyId);
        } else {
            // 有teachplanid-->修改
            resultDTO = modifyTeachplan(dto,companyId);
        }
        // 2.操作完业务需要返回结果：dto
        return resultDTO;
    }

    @Override
    public void removeTeachPlan(Long teachPlanId, Long companyId) {
        //1.判断关键数据的合法性
        Teachplan teachplan = getById(teachPlanId);
        if (ObjectUtils.isEmpty(teachplan)) ExceptionCast.cast(ContentErrorCode.E_120402);
        //1.2 判断课程基本信息是否存在
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());
        if (ObjectUtils.isEmpty(courseBase))
            ExceptionCast.cast(ContentErrorCode.E_120013);
        //1.3 判断公司 Id是否和课程中的公司Id一致
        if (!(ObjectUtils.nullSafeEquals(courseBase.getCompanyId(),companyId)))
            ExceptionCast.cast(CommonErrorCode.E_100108);
        // 2. 根据课程计划等级进行业务判断
        if (teachplan.getGrade() == TeachPlanEnum.SECEND_LEVEL) {
            // 判断二级课程计划是否有子级课程计划信息
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, teachPlanId);
            int count = count(queryWrapper);
            if (count > 0)
                ExceptionCast.cast(ContentErrorCode.E_120409);
        } else {
            // 判断三级课程计划是否关联课程媒资信息
            LambdaQueryWrapper<TeachplanMedia>
                    mediaLambdaQueryWrapper = new LambdaQueryWrapper<>();
            mediaLambdaQueryWrapper.eq(TeachplanMedia::getTeachplanId,teachPlanId);
            int mediaCount = mediaService.count(mediaLambdaQueryWrapper);
            if (mediaCount > 0)
                ExceptionCast.cast(ContentErrorCode.E_120414);
        }
        // 4.根据Id删除课程计划
        removeById(teachPlanId);
    }

    @Override
    @Transactional
    public TeachplanMediaDTO associateMedia(TeachplanMediaDTO teachplanMediaDTO, Long companyId) {

//1.判断关键数据
        // teachplanId、mediaId,companyId
        Long teachplanId = teachplanMediaDTO.getTeachplanId();
        Long mediaId = teachplanMediaDTO.getMediaId();
        if (ObjectUtils.isEmpty(teachplanId) ||
                ObjectUtils.isEmpty(mediaId) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        // 2.判断业务数据
        // 2.1 课程的计划
        // 判断课程计划是否存在：teachplanId、courseId
        // 判断是否是第三级
        //
        Teachplan teachplan = this.getById(teachplanId);
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        Integer grade = teachplan.getGrade();
        if (!(TeachPlanEnum.THIRD_LEVEL.equals(grade))) {
            ExceptionCast.cast(ContentErrorCode.E_120410);
        }
        // 2.2 课程基本信息
        // 判断是否存在或是否是同一家机构
        // 判断是否删除
        // 判断审核状态：未提交、审核未通过
        // 判断课程类型是否录播（课程基本信息中获得并判断）
        Long courseId = teachplan.getCourseId();
        // 判断是否存在或是否是同一家机构 判断是否删除
        CourseBaseDTO courseBaseDTO = courseBaseService.getCourseBaseById(courseId, companyId);
        // 判断审核状态：未提交、审核未通过
        String auditStatus = courseBaseDTO.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }
        // 判断课程类型是否录播（课程基本信息中获得并判断）
        String teachmode = courseBaseDTO.getTeachmode();
        if (!(CourseModeEnum.COURSE_MODE_RECORD_STATUS.getCode().equals(teachmode))) {
            ExceptionCast.cast(ContentErrorCode.E_120416);
        }
        // 媒资信息
        // 判断媒资信息是否存在
        // 判断是否是同一家机构
        // 判断媒资审核状态
        RestResponse<MediaDTO> response = mediaApiAgent.getById(mediaId);
        if (!(response.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(response.getCode(),response.getMsg());
        }
        // 判断媒资信息是否存在
        MediaDTO mediaDTO = response.getResult();
        if (ObjectUtils.isEmpty(mediaDTO)) {
            ExceptionCast.cast(ContentErrorCode.E_120417);
        }
        // 判断是否是同一家机构
        if (!(ObjectUtils.nullSafeEquals(mediaDTO.getCompanyId(),companyId))) {
            ExceptionCast.cast(CommonErrorCode.E_403000);
        }

        String mediaAuditStatus = mediaDTO.getAuditStatus();
        // 判断媒资审核状态
        if (!(AuditEnum.AUDIT_PASTED_STATUS.getCode().equals(mediaAuditStatus))) {
            ExceptionCast.cast(ContentErrorCode.E_120418);
        }
        // 3.将dto数据转为po数据并保存到teachplanmedia表中
        // 添加或修改
        // 如果存在：
        // 对课程计划的媒资信息进行修改：mediaId，mediaFileName
        // 如果不存在：
        // 课程计划和媒资信息的添加
        LambdaQueryWrapper<TeachplanMedia> teachplanMediaQueryWrapper = new
                LambdaQueryWrapper<>();
        teachplanMediaQueryWrapper.eq(TeachplanMedia::getTeachplanId,teachplanId);
        teachplanMediaQueryWrapper.eq(TeachplanMedia::getCourseId,courseId);
        TeachplanMedia teachplanMedia =
                teachplanMediaService.getOne(teachplanMediaQueryWrapper);
        boolean result = false;
        if (ObjectUtils.isEmpty(teachplanMedia)) {
            // 添加操作
            teachplanMedia = new TeachplanMedia();
            teachplanMedia.setMediaId(mediaId);
            teachplanMedia.setTeachplanId(teachplanId);
            teachplanMedia.setCourseId(courseId);
            teachplanMedia.setMediaFilename(mediaDTO.getFilename());
            result = teachplanMediaService.save(teachplanMedia);
        } else {
            // 修改操作: 改为UpdateWrapper
            teachplanMedia.setMediaFilename(mediaDTO.getFilename());
            teachplanMedia.setMediaId(mediaId);
            result = teachplanMediaService.updateById(teachplanMedia);
        }
        //课程计划id
        String mediaDTOType = mediaDTO.getType();
        String mediaType="";
        if (ResourceTypeEnum.VIDEO.getCode().equals(mediaDTOType)){
            LambdaUpdateWrapper<Teachplan> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(Teachplan::getId,teachplanMedia.getTeachplanId());
            updateWrapper.set(Teachplan::getMediaType,1);
            teachplanService.update(updateWrapper);
        }
        // 4.判断保存数据的结果并返回
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120411);
        }
        TeachplanMediaDTO resultDTO =
                TeachplanConvert.INSTANCE.teachplanMedia2teachplanmediaDTO(teachplanMedia);
        return resultDTO;
    }

    @Override
    //解绑
    @Transactional
    public void unboundMedia(Long companyId, Long courseBaseId, Long teachplanMediaId) {
        if (ObjectUtils.isEmpty(courseBaseId) ||
                ObjectUtils.isEmpty(teachplanMediaId) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        Long courseId = courseBaseId;
        // 判断是否存在或是否是同一家机构 判断是否删除
        CourseBaseDTO courseBaseDTO = courseBaseService.getCourseBaseById(courseId, companyId);
        // 判断审核状态：未提交、审核未通过
        String auditStatus = courseBaseDTO.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }

        int i = teachplanMediaMapper.deleteById(teachplanMediaId);
        if (i<1){
            ExceptionCast.cast(ContentErrorCode.E_120419);
        }
    }

    /**
     * 修改课程计划
     * 包括章与小结
     * 即二级与三级
     *修改不用判断其是二级还是三级
     * 因为修改必定存在，即有teachplanId值
     * 依据idj进行修改即可
     * @param dto
     * @param companyId
     * @return
     */
    private TeachplanDTO modifyTeachplan(TeachplanDTO dto, Long companyId) {
        CourseBaseDTO BaseDTO = courseBaseService.getCourseBaseById(dto.getCourseId(), companyId);
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getId, dto.getTeachPlanId());
        int count = this.count(queryWrapper);
        if (count < 1) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        LambdaUpdateWrapper<Teachplan> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Teachplan::getPname, dto.getPname());
        updateWrapper.set(Teachplan::getStartTime, dto.getStartTime());
        updateWrapper.set(Teachplan::getEndTime, dto.getEndTime());
        updateWrapper.set(Teachplan::getIsPreview, dto.getIsPreview());
        updateWrapper.eq(Teachplan::getId, dto.getTeachPlanId());
        // 4.判断修改后的结果
        boolean result = this.update(updateWrapper);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        // 5.将数据库最新的数据转为dto并返回
        Teachplan po = this.getById(dto.getTeachPlanId());
        TeachplanDTO resultDTO = TeachplanConvert.INSTANCE.entity2dto(po);
        return resultDTO;
    }

    private TeachplanDTO createTeachplan(TeachplanDTO dto, Long companyId) {
        if (ObjectUtils.isEmpty(dto.getPname())||
                ObjectUtils.isEmpty(dto.getCourseId())||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否是同一家教学机构
        //         判断是否删除
        //         判断审核状态：未提交、审核未通过
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(dto.getCourseId(), companyId);

        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }

        // 3.获得新增课程计划的父级数据
        //     给新增课程计划赋值
        //         grade：父级的grade+1
        //         parentid：父级的id
        //         orderby：select max(order) from teachplan where parentid = 父级id
        Teachplan parentNode = generateParentNode(dto, courseBase);

        int childrenGrade = parentNode.getGrade() + 1;
        dto.setGrade(childrenGrade+"");
        dto.setParentid(parentNode.getId());

        TeachplanMapper baseMapper = this.getBaseMapper();
        Integer orderBy = baseMapper.selectOrderByParentId(parentNode.getId());

        // 如果查询出orderBy为null --> 默认赋值为1
        // 如果查询出orderBy为不null --> orderBy+1
        if (ObjectUtils.isEmpty(orderBy)) {
            dto.setOrderby(TeachPlanEnum.FIRST_LEVEL);
        } else {
            dto.setOrderby(orderBy+1);
        }


        // 4.将dto转为po并保存到数据库中
        Teachplan teachplan = TeachplanConvert.INSTANCE.dto2entity(dto);

        boolean result = this.save(teachplan);
        //     判断保存后的结果
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }


        // 5.将数据库最新的数据转为dto并返回
        Teachplan po = this.getById(teachplan.getId());

        TeachplanDTO resultDTO = TeachplanConvert.INSTANCE.entity2dto(po);

        return resultDTO;
    }

    private void generateTreeNodes(TeachplanNode rootNode, List<TeachplanNode> nodes) {
        if (ObjectUtils.isEmpty(rootNode.getChildrenNodes())) {
            rootNode.setChildrenNodes(new ArrayList<TeachplanNode>());
        }

        //循环 nodes 集合数据
        for (TeachplanNode node : nodes) {
            //1.判断循环的当前数据的 parentId 是否等于 当前的 node 的 id 值。
            if (node.getParentid().equals(rootNode.getId())) {
                //1.1 将循环中的 node 添加到当前父级 node 集合数据中
                rootNode.getChildrenNodes().add(node);
                //1.2 判循环中的 node 的级别是的为 3级，如果不为 3级需要进行下一轮的递归操作
                if (!(node.getGrade().equals(TeachPlanEnum.THIRD_LEVEL))) {
                    generateTreeNodes(node,nodes);
                }else {
                    LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(TeachplanMedia::getTeachplanId, node.getId());
                    TeachplanMedia teachplanMedia = mediaService.getOne(queryWrapper);
                    node.setTeachplanMedia(teachplanMedia);
                }
            }
        }
    }
    private Teachplan generateParentNode(TeachplanDTO dto, CourseBaseDTO courseBase) {
        //1.判断新增数据是二级还是三级课程计划
        // 判断新增数据是否有parentId值
        // 如果有：三级课程计划
        // 如果没有：二级课程计划
        Long parentid = dto.getParentid();
        if (ObjectUtils.isEmpty(parentid)) {
            // 2.获得二级课程计划的父级数据
            // 查询数据库获得一级课程计划：courseId、parentid=0
            // 如果没有：自动创建并保存数据--数据来源 coursebase
            // 如果有：直接返回
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getCourseId, courseBase.getCourseBaseId());
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);
            Teachplan rootParent = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(rootParent)) {
                rootParent = new Teachplan();
                rootParent.setPname(courseBase.getName());
                rootParent.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                rootParent.setGrade(TeachPlanEnum.FIRST_LEVEL);
                rootParent.setDescription(courseBase.getDescription());
                rootParent.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                rootParent.setCourseId(courseBase.getCourseBaseId());
                boolean save = this.save(rootParent);
                if (!save) {
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }
            }
            return rootParent;
        } else {
            // 3.获得三级课程计划的父级数据
            // 查询数据库获得二级课程计划：parentId
            // 如果没有：直接抛出异常
            // 如果有：直接返回
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getId, parentid);
            Teachplan teachplan = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(teachplan)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }
            return teachplan;
        }
    }

}
